package ru.ifmo.ctd.intsys.afanasyeva.boosting;

import java.io.File;
import java.io.IOException;
import java.util.Arrays;

import ru.ifmo.ctd.intsys.afanasyeva.neural.digits.ImageUtils;

public class Main {
	
	public static void main(String[] args) throws IOException {
		String path = "data/sample/";
		File[] samples = (new File(path)).listFiles();
		int n = samples.length;
		int[] samplesValues = new int[n];
		double[][] inputs = new double[n][];
		getInputs(samples, samplesValues, inputs);
		
		double speed = 0.9;
		
		Classifier[] classifiers = new Classifier[10];
		for (int i = 0; i < 10; i++) {
			Classifier c = new AdaBoostedClassifier(getClassifiers(i, inputs, samplesValues, speed));
			trainClassifier(i, c, inputs, samplesValues);
			classifiers[i] = c;
		}
		
//		for (int i = 0; i < samples.length; i++) {
//			System.out.println(samples[i].getName());
//			for (int j = 0; j < classifiers.length; j++) {
//				System.out.print(String.format("%6.2f", classifiers[j].getOutput(inputs[i])) + "  ");
//			}
//			System.out.println();
//		}
		System.out.println("Training boosted: ");
		testClassifiers(inputs, samplesValues, classifiers);
		
		Classifier[] perc = new Classifier[10];
		for (int i = 0; i < 10; i++) {
			Classifier p = new Perceptron(samplesValues.length, speed);
			trainClassifier(i, p, inputs, samplesValues);
			perc[i] = p;
		}
		
		System.out.println("\nTraining perceptrons: ");
		testClassifiers(inputs, samplesValues, perc);
		
		String path1 = "data/test/";
		File[] samples1 = (new File(path1)).listFiles();
		int n1 = samples1.length;
		int[] samplesValues1 = new int[n1];
		double[][] inputs1 = new double[n1][];
		getInputs(samples1, samplesValues1, inputs1);
		
		System.out.println("\nTesting boosted: ");
		testClassifiers(inputs1, samplesValues1, classifiers);
		System.out.println("\nTesting perceptrons: ");
		testClassifiers(inputs1, samplesValues1, perc);
	}
	
	public static void testClassifiers(double[][] tests, int[] answers, Classifier[] c) {
		int error = 0;
		int total = 0;
		for (int i = 0; i < answers.length; i++) {
			System.out.println("correct answer:" + answers[i]);
			boolean guessed = true;
			for (int j = 0; j < c.length; j++) {
				double out = c[j].getOutput(tests[i]);
				double answer = answers[i] == j ? 1 : -1;
				int mistake = Math.signum(out) == answer ? 0 : 1;
				if (mistake == 1) guessed = false;
				error += mistake;
				total ++;
				System.out.print(String.format("%6.2f", out) + "  ");
			}
			if (guessed) System.out.print("guessed");
			System.out.println();
		}
		System.out.println("total: " + total + " error: " + error + " percent: " + (double) error / total);
	}
	
	public static void trainClassifier(int pattern, Classifier c, double[][] inputs, int[] samplesValues) {
		double[] answers = new double[inputs.length];
		Arrays.fill(answers, -1);
		
		for (int i = 0; i < inputs.length; i++) {
			if (samplesValues[i] == pattern) {
				answers[i] = 1;
			}
		}			
		c.train(inputs, answers, 1000);
	}
	
	/**
	 * Generates set of Perceptrons by leave-one-out training
	 * @param pattern
	 * @param inputs
	 * @param samplesValues
	 * @return
	 */
	public static Classifier[] getClassifiers(int pattern, double[][] inputs, int[] samplesValues, double speed) {
		int n = samplesValues.length;
		Classifier[] cs = new Classifier[n - 1];
		for (int i = 1; i < n; i++) {
			int savedSamples = samplesValues[i];
			double[] savedInputs = inputs[i].clone();			
			samplesValues[i] = 0;
			inputs[i] = inputs[0];
			
			Classifier c = new Perceptron(n, speed);
			trainClassifier(pattern, c, inputs, samplesValues);
			cs[i - 1] = c;
			
			samplesValues[i] = savedSamples;
			inputs[i] = savedInputs;
		}
		return cs;
	}
	
	public static void getInputs(File[] samples, int[] samplesValues, double[][] inputs) throws IOException {
		for (int i = 0; i < samples.length; i++) {
			inputs[i] = ImageUtils.imageToBits(samples[i].getPath(), 3, 5);
			samplesValues[i] = Integer.parseInt(samples[i].getName().substring(0, 1));
		}
	}
}
